package com.tbit.main.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.tbit.common.constant.PerformanceTypeConstant;
import com.tbit.common.entity.view.PageResult;
import com.tbit.main.constant.AccountConfigExtConstant;
import com.tbit.main.constant.MachineAbnormalConstant;
import com.tbit.main.constant.enums.PerformanceBizType;
import com.tbit.main.constant.enums.PerformanceRuleType;
import com.tbit.main.dao.core.AccountUserDao;
import com.tbit.main.dao.log.MoveLogDao;
import com.tbit.main.dao.log.MovePerformanceDao;
import com.tbit.main.pojo.*;
import com.tbit.main.pojo.maintain_performance.Range;
import com.tbit.main.pojo.maintain_performance.config.MovePerformanceConfig;
import com.tbit.main.pojo.maintain_performance.constant.RuleTypeConstant;
import com.tbit.main.pojo.maintain_performance.group.IntervalAndValidityBasedGroup;
import com.tbit.main.pojo.maintain_performance.rule.IntervalAndValidityBasedRule;
import com.tbit.main.pojo.maintain_performance.rule.IntervalAndValidityBasedSubRule;
import com.tbit.main.pojo.maintain_performance.rule.MoveDistanceRule;
import com.tbit.main.pojo.maintain_performance.stat.*;
import com.tbit.main.service.AccountConfigExtService;
import com.tbit.main.service.HistoryService;
import com.tbit.main.service.MachineAbnormalService;
import com.tbit.main.service.RedisService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author <a href="mailto:zheng.zebiao@tbit.com.cn">zb</a>
 * @since 2025/3/4
 */
@Slf4j
@Service
@AllArgsConstructor
public class MovePerformanceService extends ServiceImpl<MovePerformanceDao, MovePerformance> {
    final AccountUserDao accountUserDao;
    final RedisService redisService;
    final MoveLogDao moveLogDao;
    final AccountConfigExtService configExtService;
    final PerformanceRuleGroupService ruleGroupService;
    final HistoryService historyService;
    final MachineAbnormalService abnormalService;
    final MovePerformanceLogService performanceLogService;

    public List<PerformanceOverviewResp> queryOverviewPage(PerformanceOverviewReq req,AccountUser loginAccountUser) {
        //getBaseMapper().queryOverviewPage(req);
        List<PerformanceOverviewResp> list = this.getPerformanceList(req,loginAccountUser);

        if (CollUtil.isNotEmpty(list)) {
            String accountUserIds = list.stream()
                    .map(PerformanceOverviewResp::getAccountUserId)
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            Map<Integer, AccountUser> accountUserMap = Optional.ofNullable(accountUserDao.getByIds(accountUserIds))
                    .map(x -> x.stream()
                            .collect(Collectors.toMap(AccountUser::getAccountUserId, Function.identity()))
                    )
                    .orElse(Collections.emptyMap());

            for (PerformanceOverviewResp detail : list) {
                // 补充运维人员信息
                AccountUser accountUser = accountUserMap.get(detail.getAccountUserId());
                if (accountUser != null) {
                    detail.setAccountUserName(accountUser.getName());
                    detail.setAccountUserPhone(accountUser.getPhone());
                    detail.setDelFlag(accountUser.getDelFlag());
                }
            }
        }
        return list;
    }

    /**
     * 查询绩效统计
     * @param req
     * @return
     */
    public PerformanceCountResp queryPerformanceCount(PerformanceCountReq req) {
        return getBaseMapper().queryPerformanceCount(req);
    }

    public PageResult<MovePerformanceDetail> queryDetailPage(PerformanceDetailReq req) {
        try (Page<MovePerformanceDetail> page = PageHelper.startPage(req.getPageNo(), req.getPageSize())) {
            getBaseMapper().queryDetailPage(req);

            if (CollUtil.isNotEmpty(page)) {
                String accountUserIds = page.getResult().stream()
                        .map(MovePerformanceDetail::getAccountUserId)
                        .map(String::valueOf)
                        .collect(Collectors.joining(","));
                Map<Integer, AccountUser> accountUserMap = Optional.ofNullable(accountUserDao.getByIds(accountUserIds))
                        .map(x -> x.stream()
                                .collect(Collectors.toMap(AccountUser::getAccountUserId, Function.identity()))
                        )
                        .orElse(Collections.emptyMap());

                for (MovePerformanceDetail detail : page) {
                    // 补充运维人员信息
                    AccountUser accountUser = accountUserMap.get(detail.getAccountUserId());
                    if (accountUser != null) {
                        detail.setAccountUserName(accountUser.getName());
                        detail.setAccountUserPhone(accountUser.getPhone());
                    }
                }
            }
            return PageResult.convertPage(page.toPageInfo());
        }
    }

    public static final String MOVE_TYPE_KEY = "maintainPerformance:move:moveType:";

    /**
     * 缓存挪车类型，供后续挪车绩效使用
     *
     * @param machineId 车辆id
     * @param moveType  挪车类型 1-站外 2-超区
     */
    public void cacheMoveType(Integer machineId, Integer moveType) {
        redisService.add(MOVE_TYPE_KEY + machineId, moveType.toString(), 3, TimeUnit.DAYS);
    }

    /**
     * 获取缓存挪车类型
     *
     * @param machineId 车辆id
     * @return 挪车类型 1-站外 2-超区
     */
    public Integer getMoveTypeCache(Integer machineId) {
        String value = redisService.get(MOVE_TYPE_KEY + machineId);
        if (value != null) {
            return Integer.parseInt(value);
        }
        return null;
    }

    public static final String ABNORMAL_KEY = "maintainPerformance:move:abnormal:";

    /**
     * 缓存车辆异常，供后续挪车绩效使用
     *
     * @param machineId 车辆id
     */
    public void cacheAbnormal(Integer machineId) {
        List<MachineAbnormal> abnormal = abnormalService.getByMachineId(machineId);
        if (CollUtil.isEmpty(abnormal)) {
            return;
        }
        String abnormalTypes = abnormal.stream()
                .map(MachineAbnormal::getAbnormalType)
                .map(String::valueOf)
                .collect(Collectors.joining(","));
        redisService.add(ABNORMAL_KEY + machineId, abnormalTypes, 3, TimeUnit.DAYS);
    }

    /**
     * 获取缓存车辆异常，供后续挪车绩效使用
     *
     * @param machineId 车辆id
     * @return 异常类型列表
     */
    public List<Integer> getAbnormalCache(Integer machineId) {
        String value = redisService.get(ABNORMAL_KEY + machineId);
        if (value != null) {
            return Arrays.stream(value.split(","))
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * 计算并插入绩效
     *
     * @param ml 挪车记录
     */
    public void calculateAndInsertPerformance(MoveLog ml) {
        try {
            if (!Objects.equals(ml.getIsValid(), true)) {
                return;
            }

            // 检查开关
            AccountConfigExt performanceSwitch = configExtService.getParamKey(ml.getAccountId(), AccountConfigExtConstant.MOVE_PERFORMANCE);
            if (performanceSwitch == null || Objects.equals(performanceSwitch.getParamValue(), "0")) {
                return;
            }

            // 生成新绩效记录
            log.info("[挪车绩效] 生成新绩效记录, 挪车记录id: {}", ml.getMoveLogId());
            MovePerformance mp = new MovePerformance();
            mp.setMoveLogId(ml.getMoveLogId());
            mp.setAccountId(ml.getAccountId());
            Integer distance = historyService.getMileageByMachineNOAndTime(ml.getMachineNO(), ml.getStartTime(), ml.getEndTime());
            mp.setMoveTraceDistance(ObjectUtil.defaultIfNull(distance, 0));
            mp.setMoveType(getMoveTypeCache(ml.getMachineId()));
            mp.setOrderCountAfter24Hour(0);
            // 挪车前状态
            List<Integer> abnormalCache = getAbnormalCache(ml.getMachineId());
            if (!abnormalCache.isEmpty()) {
                StringBuilder abnormalTypes = new StringBuilder();
                for (Integer abnormalType : abnormalCache) {
                    abnormalTypes.append(MachineAbnormalConstant.parse(abnormalType))
                            .append(",");
                }
                mp.setBeforeStatus(abnormalTypes.substring(0, abnormalTypes.length() - 1));
            }

            // 计算绩效
            List<MovePerformanceLog> performanceLogs = new ArrayList<>();
            calculatePerformance(ml, mp, performanceLogs);

            // 绩效记录入库
            save(mp);
            // 保存绩效日志
            if (!performanceLogs.isEmpty()){
                // 填充绩效记录id
                performanceLogs.forEach(performanceLog -> performanceLog.setPerformanceId(mp.getPerformanceId()));
                performanceLogService.insertBatch(performanceLogs);
            }
        } catch (Exception e) {
            log.error("[挪车绩效] 计算绩效时发生错误, 挪车记录id: {}", ml.getMoveLogId(), e);
        }
    }

    /**
     * 计算挪车绩效
     */
    public void calculatePerformance(MoveLog ml, MovePerformance mp, List<MovePerformanceLog> performanceLogs) {
        MovePerformanceConfig config = ruleGroupService.selectBizPerformanceConfig(ml.getAccountId(), MovePerformanceConfig::fromRaw, PerformanceBizType.MOVE);

        BigDecimal performance = BigDecimal.ZERO;
        StringBuilder detail = new StringBuilder();

        // 基础绩效
        if (config.getBasic() != null &&
                config.getBasic().getEnabled() &&
                mp.getMoveType() != null) {
            log.info("[挪车绩效] 挪车记录id: {}, 适用基础绩效, 挪车类型: {}", mp.getMoveLogId(), mp.getMoveType());
            BigDecimal validPerf = mp.getMoveType() == 1 ? config.getBasic().getOutOfParkValidPerformance() : config.getBasic().getOutOfGeoValidPerformance();
            performance = performance.add(validPerf);
            detail.append("基础绩效: ")
                    .append(NumberUtil.toStr(validPerf))
                    .append("(")
                    .append(mp.getMoveType() == 1 ? "站外" : "超区")
                    .append(");");
            MovePerformanceLog performanceLog = new MovePerformanceLog();
            BeanUtil.copyProperties(mp, performanceLog,"performance", "performanceDetail", "remark");
            performanceLog.setPerformance(validPerf);
            performanceLog.setPerformanceDetail("基础绩效: "+NumberUtil.toStr(validPerf)+"("+(mp.getMoveType() == 1 ? "站外" : "超区")+");");
            performanceLog.setRuleType(PerformanceRuleType.BASIC.getValue());
            performanceLog.setRemark("MAIN-BASIC-基础-挪车类型："+(mp.getMoveType() == 1 ? "站外" : "超区")+")");
            performanceLogs.add(performanceLog);

            redisService.del(MOVE_TYPE_KEY + ml.getMachineId());
        }

        // 挪车距离
        if (config.getDistanceBased() != null && config.getDistanceBased().getEnabled()) {
            for (MoveDistanceRule subRule : config.getDistanceBased().getSubRules()) {
                if (subRule.getDistanceMeter().match(mp.getMoveTraceDistance())) {
                    log.info("[挪车绩效] 挪车记录id: {}, 适用挪车距离绩效, 挪车距离范围: {}", mp.getMoveLogId(), subRule.getDistanceMeter());
                    performance = performance.add(subRule.getPerformance());
                    detail.append("挪车距离绩效: ")
                            .append(NumberUtil.toStr(subRule.getPerformance()))
                            .append("(")
                            .append(subRule.getDistanceMeter())
                            .append("米);");
                    MovePerformanceLog performanceLog = new MovePerformanceLog();
                    BeanUtil.copyProperties(mp, performanceLog,"performance", "performanceDetail", "remark");
                    performanceLog.setPerformance(subRule.getPerformance());
                    performanceLog.setPerformanceDetail("挪车距离绩效: "+NumberUtil.toStr(subRule.getPerformance())+"("+subRule.getDistanceMeter()+"米);");
                    performanceLog.setRuleType(PerformanceRuleType.DISTANCE.getValue());
                    performanceLog.setRemark("MAIN-BASIC-挪车距离");
                    performanceLogs.add(performanceLog);
                    break;
                }
            }
        }

        mp.setPerformance(performance);
        mp.setPerformanceDetail(detail.toString());
    }

    /**
     * 根据车辆id更新7天内最近一次挪车绩效
     *
     * @param machineId 车辆id
     * @param orderNO   订单编号
     */
    public void updatePerformanceByNewOrder(Integer machineId, String orderNO) {
        MoveLog ml = moveLogDao.selectLast7DayFirstValidByMachineId(machineId);
        if (ml == null) {
            return;
        }

        // 检查开关
        AccountConfigExt performanceSwitch = configExtService.getParamKey(ml.getAccountId(), AccountConfigExtConstant.MOVE_PERFORMANCE);
        if (performanceSwitch == null || Objects.equals(performanceSwitch.getParamValue(), "0")) {
            return;
        }

        try {
            // 获取绩效记录
            MovePerformance mp = getOne(Wrappers.<MovePerformance>query()
                    .eq(MovePerformance.Fields.moveLogId, ml.getMoveLogId())
            );
            if (mp == null  || Objects.equals(mp.getPerformanceType(), PerformanceTypeConstant.MANUAL)){
                log.info("[挪车绩效] 挪车记录id: {}, 绩效不存在或者是手动绩效,不处理。", ml.getMoveLogId());
                return;
            }

            // 获取配置
            MovePerformanceConfig config = ruleGroupService.selectBizPerformanceConfig(ml.getAccountId(), MovePerformanceConfig::fromRaw, PerformanceBizType.MOVE);
            if ((config.getIntervalAndValidityBased() == null || !config.getIntervalAndValidityBased().getEnabled())) {
                return;
            }

            LocalDateTime dispatchTime = DateUtil.parseLocalDateTime(ml.getEndTime());
            LocalDateTime orderTime = LocalDateTime.now();
            LocalTime dispatchLocalTime = dispatchTime.toLocalTime();

            BigDecimal performance = mp.getPerformance();
            StringBuilder detail = new StringBuilder(mp.getPerformanceDetail());

            // 挪车时间是否在时段、时效规则区间内
            IntervalAndValidityBasedGroup intervalAndValidityBasedGroup = config.getIntervalAndValidityBased();
            // 判断是否超过最大订单数限制 是否开启该规则
            if (intervalAndValidityBasedGroup != null
                    && intervalAndValidityBasedGroup.getEnabled()
                    && (intervalAndValidityBasedGroup.getMaxOrderCount() > mp.getOrderCountAfter24Hour())){
                List<MovePerformanceLog> performanceLogs = new ArrayList<>(1);
                List<IntervalAndValidityBasedRule> rules = intervalAndValidityBasedGroup.getRules();
                for (IntervalAndValidityBasedRule rule : rules) {
                    Range<String> completedDuration = rule.getCompletedDuration();
                    // completedDuration 转换为LocalTime去比较
                    LocalTime start = LocalTime.parse(completedDuration.getStart2Calc());
                    LocalTime end = LocalTime.parse(completedDuration.getEnd2Calc());
                    Range completedRange = new Range<LocalTime>();
                    completedRange.setStart(start);
                    completedRange.setEnd(end);
                    if (completedRange.match(dispatchLocalTime)){
                        // 挪车时间在此时间内
                        String ruleType = rule.getRuleType();
                        switch (ruleType){
                            case RuleTypeConstant.INTERVAL_BASED_RULE:// 基于时间间隔的规则
                                IntervalAndValidityBasedSubRule subRule = rule.calculatePerformance(dispatchTime,orderTime);
                                if (subRule != null){
                                    log.info("[挪车绩效] 更新挪车记录id: {}, 适用订单时段绩效, 时段: {}, 订单号: {}", mp.getMoveLogId(), subRule.getOrderIntervalOrHour(), orderNO);
                                    performance = performance.add(subRule.getPerformance());
                                    detail.append("产生订单时段绩效: ")
                                            .append(NumberUtil.toStr(subRule.getPerformance()))
                                            .append("(")
                                            .append(subRule.getOrderIntervalOrHour())
                                            .append(");");
                                    MovePerformanceLog performanceLog = new MovePerformanceLog();
                                    BeanUtil.copyProperties(mp, performanceLog,"performance", "performanceDetail", "remark");
                                    performanceLog.setPerformanceId(mp.getPerformanceId());
                                    performanceLog.setPerformance(subRule.getPerformance());
                                    performanceLog.setPerformanceDetail("产生订单时段绩效: "+NumberUtil.toStr(subRule.getPerformance())+"("+subRule.getOrderIntervalOrHour()+";");
                                    performanceLog.setRuleType(PerformanceRuleType.INTERVAL.getValue());
                                    performanceLog.setFirstOrderTime((int) Duration.between(dispatchTime, orderTime).toMinutes());
                                    performanceLog.setOrderCountAfter24Hour(mp.getOrderCountAfter24Hour() + 1);
                                    performanceLog.setRemark("MAIN-INTERVAL_BASED_RULE-产生订单时段");
                                    performanceLogs.add(performanceLog);
                                }
                                break;
                            case RuleTypeConstant.VALIDITY_BASED_RULE: //基于时效的规则
                                long hours = Duration.between(dispatchTime, orderTime).toHours();
                                IntervalAndValidityBasedSubRule subValidityBasedRule = rule.calculatePerformance((int) hours);
                                if (subValidityBasedRule != null){
                                    log.info("[挪车绩效] 更新挪车记录id: {}, 适用订单时效绩效, 时效: {}h, 订单号: {}", mp.getMoveLogId(), subValidityBasedRule.getOrderIntervalOrHour(), orderNO);
                                    performance = performance.add(subValidityBasedRule.getPerformance());
                                    detail.append("产生订单时效绩效: ")
                                            .append(NumberUtil.toStr(subValidityBasedRule.getPerformance()))
                                            .append("(")
                                            .append(subValidityBasedRule.getOrderIntervalOrHour())
                                            .append("小时);");
                                    MovePerformanceLog performanceLog = new MovePerformanceLog();
                                    BeanUtil.copyProperties(mp, performanceLog,"performance", "performanceDetail", "remark");
                                    performanceLog.setPerformanceId(mp.getPerformanceId());
                                    performanceLog.setPerformance(subValidityBasedRule.getPerformance());
                                    performanceLog.setPerformanceDetail("产生订单时效绩效: "+NumberUtil.toStr(subValidityBasedRule.getPerformance())+"("+subValidityBasedRule.getOrderIntervalOrHour()+";");
                                    performanceLog.setRuleType(PerformanceRuleType.VALIDITY.getValue());
                                    performanceLog.setFirstOrderTime((int) Duration.between(dispatchTime, orderTime).toMinutes());
                                    performanceLog.setOrderCountAfter24Hour(mp.getOrderCountAfter24Hour() + 1);
                                    performanceLog.setRemark("MAIN-VALIDITY_BASED_RULE-产生订单时效");
                                    performanceLogs.add(performanceLog);
                                }
                                break;
                            default:
                                break;
                        }
                    }

                }
                // 保存绩效日志
                if (!performanceLogs.isEmpty()){
                    performanceLogService.insertBatch(performanceLogs);
                }
            }

            // 更新订单数等信息
            if (mp.getFirstOrderTime() == null) {
                mp.setFirstOrderTime((int) Duration.between(dispatchTime, orderTime).toMinutes());
            }
            mp.setOrderCountAfter24Hour(mp.getOrderCountAfter24Hour() + 1);

            // 更新记录
            mp.setPerformance(performance);
            mp.setPerformanceDetail(detail.toString());
            updateById(mp);

        } catch (Exception e) {
            log.error("[挪车绩效] 更新绩效时发生错误, 挪车记录id: {}", ml.getMoveLogId(), e);
        }
    }

    /**
     * 更新挪车记录的绩效
     *
     * @param performanceId 绩效记录id
     * @param performance   绩效值
     * @param remark        备注
     */
    @Transactional("logTransactionManager")
    public void updatePerformance(Long performanceId, BigDecimal performance, String remark) {
        MovePerformance update = getById(performanceId);
        BigDecimal oldPerformance = update.getPerformance();
        update.setPerformanceId(performanceId);
        update.setPerformance(performance);
        update.setRemark(remark);
        update.setPerformanceDetail("人工修改绩效: 总绩效" + NumberUtil.toStr(performance, true));
        update.setPerformanceType(PerformanceTypeConstant.MANUAL);
        updateById(update);
        // 计算新旧绩效差值，用于新增一条绩效日志记录move_performance_log
        BigDecimal subPerformance = performance.subtract(oldPerformance);
        if (subPerformance.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        MovePerformanceLog log = new MovePerformanceLog();
        BeanUtil.copyProperties(update, log, "remark");
        log.setPerformance(subPerformance);
        log.setRuleType(PerformanceRuleType.MANUAL.getValue());
        log.setRemark("MAIN-人工修改绩效: " + NumberUtil.toStr(oldPerformance, true) + " -> " + NumberUtil.toStr(performance, true));
        performanceLogService.save(log);
    }

    /**
     * @description: 根据设置的当前用户的运维权限获取绩效数据
     * @author: mo.shanyong
     * @date: 2025/5/12 11:13
     * @param: req
     * @param: loginAccountUser
     * @return: java.util.List<com.tbit.main.pojo.maintain_performance.stat.PerformanceOverviewResp>
     */
    private List<PerformanceOverviewResp> getPerformanceList(PerformanceOverviewReq req,AccountUser loginAccountUser){
        //数据权限（0全部，1仅自己）
        req.setAccountUserId(Objects.equals(loginAccountUser.getRole().getDataPermission(),1) ? loginAccountUser.getAccountUserId() : null);
        return getBaseMapper().queryOverviewPage(req);
    }
}
